package com.yic.module.device.service.fault;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.yic.framework.security.core.util.SecurityFrameworkUtils;
import com.yic.module.device.controller.admin.resume.vo.ResumeCreateReqVO;
import com.yic.module.device.dal.dataobject.device.DeviceDO;
import com.yic.module.device.service.device.DeviceService;
import com.yic.module.device.service.faulthandle.FaultHandleService;
import com.yic.module.device.service.resume.ResumeService;
import com.yic.module.system.api.user.AdminUserApi;
import com.yic.module.system.api.user.dto.AdminUserRespDTO;
import com.yic.module.system.enums.dict.DeviceFaultStatusEnum;
import com.yic.module.system.enums.dict.DeviceResumeTypeEnum;
import com.yic.module.system.enums.dict.DeviceStatusEnum;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import com.yic.module.device.controller.admin.fault.vo.*;
import com.yic.module.device.dal.dataobject.fault.FaultDO;
import com.yic.framework.common.pojo.PageResult;

import com.yic.module.device.convert.fault.FaultConvert;
import com.yic.module.device.dal.mysql.fault.FaultMapper;

import static com.yic.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yic.module.device.enums.ErrorCodeConstants.*;

/**
 * 设备报修 Service 实现类
 *
 * @author 系统管理员
 */
@Service
@Validated
public class FaultServiceImpl implements FaultService {

    @Resource
    private FaultMapper faultMapper;
    @Resource
    private DeviceService deviceService;
    @Resource
    private ResumeService resumeService;
    @Resource
    private AdminUserApi adminUserApi;

    @Override
    @Transactional
    public Long createFault(FaultCreateReqVO createReqVO) {
        validateCreateOrUpdate(null, createReqVO.getCode());
        // 插入
        FaultDO faultDO = FaultConvert.INSTANCE.convert(createReqVO);
        FaultDO fault = packFault(faultDO);
        faultMapper.insert(fault);
        deviceService.updateDeviceStatus(createReqVO.getDeviceId(), DeviceStatusEnum.FAULT.getDeviceStatus(), null);
        // 返回
        return fault.getId();
    }

    /**
     * 组装报修信息
     * @param fault
     * @return
     */
    private FaultDO packFault(FaultDO fault) {
        if (ObjectUtil.isEmpty(fault)) {
            return fault;
        }
        // 组装设备信息
        DeviceDO device = deviceService.get(fault.getDeviceId());
        // 校验设备是否已经报修
        if (DeviceStatusEnum.FAULT.getDeviceStatus().equals(device.getStatus())) {
            throw exception(FAULT_EXISTS);
        }
        fault.setFactoryId(device.getFactoryId());
        fault.setCenterId(device.getCenterId());
        fault.setFaultTime(LocalDateTime.now());
        fault.setStatus(DeviceFaultStatusEnum.COMMIT.getDeviceFaultStatus());
        // 组装用户信息
        AdminUserRespDTO user = adminUserApi.getUser(SecurityFrameworkUtils.getLoginUserId());
        fault.setUserId(user.getId());
        fault.setDeptId(user.getDeptId());
        return fault;
    }

    @Override
    public void updateFault(FaultUpdateReqVO updateReqVO) {
        // 校验
        FaultDO fault = validateCreateOrUpdate(updateReqVO.getId(), updateReqVO.getCode());
        if (!DeviceFaultStatusEnum.COMMIT.getDeviceFaultStatus().equals(fault.getStatus())) {
            throw exception(FAULT_NOT_CHANGE);
        }
        // 更新
        FaultDO updateObj = FaultConvert.INSTANCE.convert(updateReqVO);
        faultMapper.updateById(updateObj);
    }

    @Override
    @Transactional
    public void deleteFault(Long id) {
        // 校验
        FaultDO fault = validateFaultExists(id);
        if (!DeviceFaultStatusEnum.COMMIT.getDeviceFaultStatus().equals(fault.getStatus())) {
            throw exception(FAULT_NOT_CHANGE);
        }
        // 删除
        faultMapper.deleteById(id);
        deviceService.updateDeviceStatus(id, DeviceStatusEnum.NORMAL.getDeviceStatus(), null);
    }

    private FaultDO validateCreateOrUpdate(Long id, String code){
        return validateCodeExist(id, code);
    }

    private FaultDO validateCodeExist(Long id, String code){
        FaultDO faultDO = faultMapper.selectByCode(code);
        if (ObjectUtil.isEmpty(faultDO)){
            return faultDO;
        }
        if (id == null){
            throw exception(FAULT_CODE_EXISTS);
        }
        if (!id.equals(faultDO.getId())){
            throw exception(FAULT_CODE_EXISTS);
        }
        return faultDO;
    }

    private FaultDO validateFaultExists(Long id) {
        FaultDO fault = faultMapper.selectById(id);
        if (fault == null) {
            throw exception(FAULT_NOT_EXISTS);
        }
        if (!DeviceFaultStatusEnum.COMMIT.getDeviceFaultStatus().equals(fault.getStatus())) {
            throw exception(FAULT_NOT_CHANGE);
        }
        return fault;
    }

    @Override
    public FaultDO getFault(Long id) {
        return faultMapper.selectById(id);
    }

    @Override
    public List<FaultDO> getFaultList(Collection<Long> ids) {
        return faultMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<FaultDO> getFaultPage(FaultPageReqVO pageReqVO) {
        return faultMapper.selectPage(pageReqVO);
    }

    @Override
    public List<FaultDO> getFaultList(FaultExportReqVO exportReqVO) {
        return faultMapper.selectList(exportReqVO);
    }

    @Override
    public void acceptFault(Long id) {
        FaultDO fault = validateAcceptFault(id);
        fault.setStatus(DeviceFaultStatusEnum.HANDLE.getDeviceFaultStatus());
        fault.setStartTime(LocalDateTime.now());
        fault.setHandleUser(SecurityFrameworkUtils.getLoginUserId());
        faultMapper.updateById(fault);
    }

    @Override
    public void handleFault(Long id) {
        FaultDO fault = validateHandleFault(id);
        fault.setStatus(DeviceFaultStatusEnum.HANDLED.getDeviceFaultStatus());
        fault.setEndTime(LocalDateTime.now());
        fault.setDuration(new BigDecimal(String.valueOf(DateUtil.between(new DateTime(fault.getStartTime()), new DateTime(fault.getEndTime()), DateUnit.HOUR))));
        faultMapper.updateById(fault);
    }

    @Override
    @Transactional
    public void confirmFault(Long id) {
        FaultDO fault = validateConfirmFault(id);
        fault.setStatus(DeviceFaultStatusEnum.CONFIRM.getDeviceFaultStatus());
        fault.setConfirmTime(LocalDateTime.now());
        faultMapper.updateById(fault);
        deviceService.updateDeviceStatus(id, DeviceStatusEnum.NORMAL.getDeviceStatus(), null);
        resumeService.createResume(fault.getDeviceId(), DeviceResumeTypeEnum.FAULT.getDeviceResumeType(), "设备报修：" + fault.getDescription(), fault.getId(), fault.getUserId(), fault.getFaultTime());
    }

    private FaultDO validateAcceptFault(Long id) {
        FaultDO faultDO = faultMapper.selectById(id);
        if (ObjectUtil.isEmpty(faultDO)) {
            throw exception(FAULT_NOT_EXISTS);
        }
        if (!DeviceFaultStatusEnum.COMMIT.getDeviceFaultStatus().equals(faultDO.getStatus())) {
            // 只允许接受待处理的工单
            throw exception(FAULT_NOT_ACCEPT);
        }
        return faultDO;
    }

    private FaultDO validateHandleFault(Long id) {
        FaultDO faultDO = faultMapper.selectById(id);
        if (ObjectUtil.isEmpty(faultDO)) {
            throw exception(FAULT_NOT_EXISTS);
        }
        if (!DeviceFaultStatusEnum.HANDLE.getDeviceFaultStatus().equals(faultDO.getStatus())) {
            throw exception(FAULT_NOT_HANDLE);
        }
        if (!SecurityFrameworkUtils.getLoginUserId().equals(faultDO.getHandleUser())) {
            throw exception(FAULT_NOT_ME);
        }
        return faultDO;
    }

    private FaultDO validateConfirmFault(Long id) {
        FaultDO faultDO = faultMapper.selectById(id);
        if (ObjectUtil.isEmpty(faultDO)) {
            throw exception(FAULT_NOT_EXISTS);
        }
        if (!DeviceFaultStatusEnum.HANDLED.getDeviceFaultStatus().equals(faultDO.getStatus())) {
            // 只允许接受待处理的工单
            throw exception(FAULT_NOT_CONFIRM);
        }
        if (!SecurityFrameworkUtils.getLoginUserId().equals(faultDO.getUserId())) {
            throw exception(FAULT_NOT_ME);
        }
        return faultDO;
    }

}
